/*
	    (c) Copyright 1998-2000 - Tord Jansson
	    ======================================

	This file is part of the BladeEnc MP3 Encoder, based on
	ISO's reference code for MPEG Layer 3 compression, and might
	contain smaller or larger sections that are directly taken
	from ISO's reference code.

	All changes to the ISO reference code herein are either
	copyrighted by Tord Jansson (tord.jansson@swipnet.se)
	or sublicensed to Tord Jansson by a third party.

    BladeEnc is free software; you can redistribute this file
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.



    ------------    CHANGES    ------------

    2000-11-22  Andre Piotrowski

    -	big fix: module - original dist10 error - l3_sb_sample has to be of type 'L3SBS' not 'SBS'.
    -	bug fix: codecInit() - don't reset fInit-huffman_read_flag - read_huffcodetab() is a memory eater!

    2000-12-11  ap

    -	reformatted
    -	speed up: use single buffer
*/

#include    <stdlib.h>
#include    <assert.h>

#include    "common.h"
#include    "l3psy.h"
#include    "mdct.h"
#include    "reservoir.h"
#include    "formatbitstream2.h"
#include    "l3bitstream.h"
#include    "loop.h"
#include    "encoder.h"
#include    "codec.h"





extern	int		fInit_fft;





/************************************************************************/

#define	SAMPLES_PER_FRAME	1152

static	L3SBS			l3_sb_sample;

static	layer			info;



#if ORG_BUFFERS
static	short			buffer[2][1152];
/*  static  float		    snr32[32]; */
static	short			sam[2][2048];
#else
static	FLOAT			buffer[2][2048];
static	int			buffer_idx;
#endif



static	int			whole_SpF;

static	double			frac_SpF, slot_lag;

static	int			stereo, error_protection;

static	III_side_info_t		l3_side;
static	CodecInitOut		sOut;

static	frame_params		fr_ps;



char			*pEncodedOutput;
int			outputBit;

volatile double		avg_slots_per_frame;





/*____ codecInit() ____________________________________________________________*/

CodecInitOut		*codecInit (CodecInitIn *psIn)
{
    int			    j;

    /* Read psIn */

    switch (psIn->frequency)
    {
	case 48000:  info.sampling_frequency = 1;  break;
	case 44100:  info.sampling_frequency = 0;  break;
	case 32000:  info.sampling_frequency = 2;  break;
	default   :  return FALSE;
    }

    switch (psIn->mode)
    {
	case  0:  info.mode = MPG_MD_STEREO      ;  info.mode_ext = 0;  break;
	case  2:  info.mode = MPG_MD_DUAL_CHANNEL;  info.mode_ext = 0;  break;
	case  3:  info.mode = MPG_MD_MONO        ;  info.mode_ext = 0;  break;
	default:  return FALSE;
    }

    j = 0;
    while (j < 15  &&  bitratex[1][j] != psIn->bitrate)
	j++;
    info.bitrate_index    = j;

    info.version	  = 1;   /* Default: MPEG-1 */
    info.emphasis	  = psIn->emphasis;
    info.extension	  = psIn->fPrivate;
    info.copyright	  = psIn->fCopyright;
    info.original	  = psIn->fOriginal;
    info.error_protection = psIn->fCRC;


/*_______ Static-fix _______________*/


    fInit_mdct_sub = 0;
    fInit_mdct = 0;

    fInit_fft = 0;

    fInit_iteration_loop = 0;
/*  DON'T DO THAT ALL THE TIME
    fInit_huffman_read_flag = 0;
*/

    fixStatic_loop();

    l3_side.main_data_begin = 0;
    fixStatic_reservoir();


/*___________________________________*/

    psycho_anal_init (psIn->frequency);
    initWindowFilterSubband ();
    initFormatBitstream ();

/*     clear buffers */
    memset ((char *) l3_sb_sample, 0, sizeof(l3_sb_sample));
    memset((char *) buffer, 0, sizeof(buffer));
/*    memset((char *) snr32, 0, sizeof(snr32));*/
#if ORG_BUFFERS
    memset((char *) sam, 0, sizeof(sam));
#endif



    fr_ps.header      = &info;
    fr_ps.tab_num     = -1;   /* no table loaded */
    fr_ps.alloc	      = NULL;
    fr_ps.actual_mode = info.mode;
    fr_ps.stereo      = (info.mode == MPG_MD_MONO) ? 1 : 2;
    fr_ps.sblimit     = SBLIMIT;
    fr_ps.jsbound     = SBLIMIT;

    
    stereo = fr_ps.stereo;
    error_protection = info.error_protection;

    avg_slots_per_frame =
	((double) SAMPLES_PER_FRAME / s_freq[1][info.sampling_frequency]) *
	((double) bitratex[1][info.bitrate_index] / 8.0);
    whole_SpF = (int) avg_slots_per_frame;
    frac_SpF  = avg_slots_per_frame - (double) whole_SpF;
    slot_lag  = -frac_SpF;

/*    if (frac_SpF == 0)
	info.padding = 0;
*/
    genNoisePowTab();

/*________________________*/


    if( stereo != 2 )
	sOut.nSamples = SAMPLES_PER_FRAME;
    else
	sOut.nSamples = SAMPLES_PER_FRAME*2;

    sOut.bufferSize = 2048;

    return  &sOut;	    /* How many samples we want in each chunk... */
}





/*____ codecEncodeChunk() _____________________________________________________*/

unsigned int		codecEncodeChunk
(
    int			    nSamples,
    short		    *pSamples,
    char		    *pDest
)
{
    static  double		xr[2][2][576];
    static  double		xr_dec[2][2][576];
    static  double		pe[2][2];
    static  int			l3_enc[2][2][576];
    static  III_psy_ratio	ratio;
    static  III_scalefac_t	scalefac;
    int				gr, ch;
    int				mean_bits, sideinfo_len;
    int				bitsPerFrame;
    int				j;

/*
    static  int			fFirst = TRUE;

    if (fFirst)
    {
	memset ((char *) &xr      , 0, sizeof(xr));
	memset ((char *) &xr_dec  , 0, sizeof(xr_dec));
	memset ((char *) &pe      , 0, sizeof(pe));
	memset ((char *) &l3_enc  , 0, sizeof(l3_enc));
	memset ((char *) &ratio   , 0, sizeof(ratio));
	memset ((char *) &l3_side , 0, sizeof(l3_side));
	memset ((char *) &scalefac, 0, sizeof(scalefac));

	fFirst = FALSE;
    }
*/


/* rebuffer audio */

#if ORG_BUFFERS
    rebuffer_audio (buffer, pSamples, nSamples, stereo);
#else
    rebuffer_audio (pSamples, buffer, &buffer_idx, nSamples, stereo);
#endif


/* psychoacoustic model */

    for (gr = 0;  gr < 2;  gr++)
	for (ch = 0;  ch < stereo;  ch++)
	    psycho_anal
	    (
#if ORG_BUFFERS
		&buffer[ch][gr*576],
		&sam[ch][0],
#else
		buffer[ch],
		(buffer_idx+gr*576) & 2047,
#endif
		ch,
		3,
		/*snr32,*/
		&ratio.l[gr][ch][0],
		&ratio.s[gr][ch][0],
		&pe[gr][ch],
		&l3_side.gr[gr].ch[ch].tt
	    );


/* polyphase filtering */

    for (gr = 0;  gr < 2;  gr++)
    {
	int	gr_plus_1 = gr_idx[gr+1];

	for (ch = 0;  ch < stereo;  ch++)
	{
	    for (j = 0;  j < 18;  j++)
	    {
		windowFilterSubband
		(
#if ORG_BUFFERS
		    &buffer[ch][gr*18*32+32*j],
		    ch,
#else
		    buffer[ch],
		    (buffer_idx+768-480+gr*18*32+32*j) & 2047,
#endif
		    l3_sb_sample[ch][gr_plus_1][j]
		);
	    }
	}
    }


/* apply mdct to the polyphase outputs */

    mdct_sub (&l3_sb_sample, xr, stereo, &l3_side, 2);


    pEncodedOutput = pDest;
    outputBit = 8;
    pEncodedOutput[0] = 0;


    if (frac_SpF != 0)
    {
	if (slot_lag > (frac_SpF-1.0))
	{
	    slot_lag -= frac_SpF;
	    info.padding = 0;
	}
	else
	{
	    info.padding = 1;
	    slot_lag += (1-frac_SpF);
	}
    }

    bitsPerFrame = 8 * whole_SpF + (info.padding * 8);


/* determine the mean bitrate for main data */

    sideinfo_len = 32;

    if (stereo == 1)
	sideinfo_len += 136;
    else
	sideinfo_len += 256;

    if (info.error_protection)
	sideinfo_len += 16;

    mean_bits = (bitsPerFrame - sideinfo_len) / 2;


/* bit and noise allocation */

    iteration_loop
    (
	pe,
	xr,
	&ratio,
	&l3_side,
	l3_enc,
	mean_bits,
	stereo,
	xr_dec,
	&scalefac,
	&fr_ps,
	0,
	bitsPerFrame
    );


/* write the frame to the bitstream */

    III_format_bitstream
    (
	bitsPerFrame,
	&fr_ps,
	l3_enc,
	&l3_side,
	&scalefac,
	xr,
	NULL,
	0
    );


    return  pEncodedOutput - pDest;
}





/*____ codecExit() ____________________________________________________________*/

unsigned int		codecExit (char *pDest)
{
    pEncodedOutput = pDest;
    outputBit = 8;
    pEncodedOutput[0] = 0;

    psycho_anal_exit ();
    exitFormatBitstream ();
    III_FlushBitstream ();

    return pEncodedOutput - pDest;
}





/*____ codecFlush() _________________________________________________________*/

unsigned int		codecFlush (char *pDest)
{
    pEncodedOutput = pDest;
    outputBit = 8;
    pEncodedOutput[0] = 0;

    flushFrame ();

    whole_SpF = (int) avg_slots_per_frame;
    frac_SpF  = avg_slots_per_frame - (double) whole_SpF;
    slot_lag  = -frac_SpF;

    l3_side.main_data_begin = 0;
    fixStatic_reservoir ();

    return pEncodedOutput - pDest;
}



